using System;
using System.Collections.Generic;
using System.Linq;
using System.Timers;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;


namespace Pacman
{
    /// <summary>
    /// Type principal pour votre jeu
    /// </summary>
    public class Jeu : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        private int level;
        private int score;
        private int vies;

        private const int INITIAL_NB_BEAN = 298;
        private byte[,] map;
        private int beanCpt;

        private Texture2D mur;
        private Texture2D bean;
        private Texture2D gros_bean;
        private Texture2D[] tVies;
        private SpriteFont font;
        private TextePositionne texteScore;
        private TextePositionne texteLevel;
        private TextePositionne texteGameOver;
        private String contenuTexteGameOver;
        private TextePositionne texteToStart;
        private String contenuTexteToStart;

        private Pacman pacman;
        private Fantome[] tabFantome;
        private Sommet[,] mesSommets;

        private bool haveStart; // En attente de Entrer
        private bool havePause; // En attente de Entrer
        private bool isClignotement; // Fantome qui clignotent
        private bool isFantomePeur; // Fantome peut être mangé
        private int stateDead; // Etat animation mort
        private int stateDecompteStart; // Etat animation start
        private TimeSpan timeEndDecompte = TimeSpan.Zero; // Timer
        private TimeSpan timeLastEnter = TimeSpan.Zero; // temps entre 2 Entrer
        private TimeSpan timeFinFantomePeur = TimeSpan.Zero; // timing fin 'fantome eur'
        private TimeSpan timeMusiqueFond = TimeSpan.Zero; // timing de la musique de fond

        private SoundEffect soundIntro;
        private SoundEffect soundComplete;
        private SoundEffect soundInGame;

        public Jeu()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            
            NewLevel(2);
            tabFantome = new Fantome[4];
            tVies = new Texture2D[vies];

            mesSommets = new Sommet[map.GetLength(0),map.GetLength(1)];
            //Initialisation des sommets de la matrice
            for (int i =0;i<map.GetLength(0);i++){
                for (int j = 0; j < map.GetLength(1); j++)
                {
                    if (map[i, j] != 0)
                    {
                        mesSommets[i, j] = new Sommet(i,j);
                    }
                }
            }


        }//Jeu()



        /// <summary>
        /// Permet au jeu d’effectuer l’initialisation nécessaire pour l’exécution.
        /// Il peut faire appel aux services et charger tout contenu
        /// non graphique. Calling base.Initialize énumère les composants
        /// et les initialise.
        /// </summary>
        protected override void Initialize()
        {
            this.TargetElapsedTime = TimeSpan.FromSeconds(0.02); //Modifie le taux de rafraichissement du jeu (defaut : 0.02)
            base.Initialize();
        }//Initialize()



        /// <summary>
        /// LoadContent est appelé une fois par partie. Emplacement de chargement
        /// de tout votre contenu.
        /// </summary>
        protected override void LoadContent()
        {
            // Créer un SpriteBatch, qui peut être utilisé pour dessiner des textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            graphics.PreferredBackBufferWidth = 1024;
            graphics.PreferredBackBufferHeight = 620;
            graphics.ApplyChanges();

            mur = Content.Load<Texture2D>("images/mur");
            bean = Content.Load<Texture2D>("images/bean");
            gros_bean = Content.Load<Texture2D>("images/gros_bean");
            for (int i = 0; i < vies; i++)
                tVies[i] = Content.Load<Texture2D>("images/pacman_1");

            soundIntro = Content.Load<SoundEffect>("sons/LevelIntro");
            soundComplete = Content.Load<SoundEffect>("sons/LevelComplete");
            soundInGame = Content.Load<SoundEffect>("sons/InGame");

            font = Content.Load<SpriteFont>("SpriteFont");
            texteScore = new TextePositionne(font, "Score : " + score, new Vector2(29 * 20, 50), Color.AntiqueWhite);
            texteLevel = new TextePositionne(font, "", new Vector2(29 * 20, 80), Color.AntiqueWhite);
            texteToStart = new TextePositionne(font, "", new Vector2(29 * 20, graphics.PreferredBackBufferHeight - 50), Color.Red);
            texteGameOver = new TextePositionne(font, "", new Vector2(29 * 20, graphics.PreferredBackBufferHeight / 2), Color.Red);

            pacman = new Pacman(this);

            tabFantome[0] = new Fantome(this, "Rouge");
            tabFantome[1] = new Fantome(this, "Rose");
            tabFantome[2] = new Fantome(this, "Bleu");
            tabFantome[3] = new Fantome(this, "Vert");

            base.LoadContent();
        }//LoadContent()



        /// <summary>
        /// UnloadContent est appelé une fois par partie. Emplacement de déchargement
        /// de tout votre contenu.
        /// </summary>
        protected override void UnloadContent()
        {        }//UnloadContent()



        /// <summary>
        /// Permet au jeu d’exécuter la logique de mise à jour du monde,
        /// de vérifier les collisions, de gérer les entrées et de lire l’audio.
        /// </summary>
        /// <param name="gameTime">Fournit un aperçu des valeurs de temps.</param>
        protected override void Update(GameTime gameTime)
        {
            // Permet au jeu de se fermer
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            // Pause ou commencer
            KeyboardState keyboard = Keyboard.GetState();
            if (keyboard.IsKeyDown(Keys.Enter) && gameTime.TotalGameTime - timeLastEnter > TimeSpan.FromSeconds(1))
            {
                timeLastEnter = gameTime.TotalGameTime;
                if (!haveStart)
                {
                    NewLevel(2);
                    haveStart = !haveStart;
                }
                else
                {
                    havePause = !havePause;
                    if (havePause)
                    {
                        contenuTexteToStart = "Tapez Entrer pour continuer.";
                        stateDecompteStart = 3;
                    }
                }
                contenuTexteGameOver = "";
            }
            
            //Musique de fond
            if (timeMusiqueFond != TimeSpan.Zero && timeMusiqueFond < gameTime.TotalGameTime)
                soundInGame.Play();
            
            // Le jeu n'a pas commencé
            if (!haveStart || havePause)
            {
                //contenuTexteToStart = "Tapez Entrer pour commencer.";
            }
            // Décompte du début
            else if (stateDecompteStart >= 0)
            {
                if (stateDecompteStart == 0) // Fin 
                {
                    timeEndDecompte = TimeSpan.Zero;
                    stateDecompteStart--;
                    soundInGame.Play();
                    timeMusiqueFond = gameTime.TotalGameTime + soundInGame.Duration;
                }
                else if (gameTime.TotalGameTime > timeEndDecompte) 
                {
                    if (stateDecompteStart >= 3)
                    {  // Début
                        timeEndDecompte = gameTime.TotalGameTime + soundIntro.Duration;
                        soundIntro.Play();
                        stateDecompteStart--;
                        contenuTexteToStart = "";
                        contenuTexteToStart = "Tapez Entrer pour faire pause.";
                    }
                    else 
                        stateDecompteStart = 0;
                    /*contenuTexteDecompte = stateDecompteStart--.ToString();
                    contenuTexteToStart = "";**/
                }
            }
            // Animation mort pacman
            else if (stateDead > 0)
            {
                if (gameTime.TotalGameTime > timeEndDecompte)
                {
                    if (stateDead > 3)
                    {
                        timeEndDecompte = TimeSpan.Zero;
                        NewLevel(1);
                    } 
                    else 
                        timeEndDecompte = gameTime.TotalGameTime + TimeSpan.FromTicks(pacman.goOnDie(stateDead++).Ticks / 6);
                }
            }
            // Jeu normal
            else
            {
                // Gestion 'Fantome peur'
                if (isFantomePeur)
                {
                    if (timeFinFantomePeur == TimeSpan.Zero)
                        timeFinFantomePeur = gameTime.TotalGameTime + TimeSpan.FromSeconds(10);
                    else if (gameTime.TotalGameTime > timeFinFantomePeur)
                    {
                        timeFinFantomePeur = TimeSpan.Zero;
                        goOnFantomePeur(false);
                    }
                    else if (timeFinFantomePeur - gameTime.TotalGameTime < TimeSpan.FromSeconds(3))
                        isClignotement = true;
                }
                // Animation
                int iFantome = pacman.Animer();
                if (iFantome > 0)
                {
                    if (tabFantome[iFantome].Mvt is MouvementFuite)
                    {
                        score += 100;
                        tabFantome[iFantome].reInitialize();
                    }
                    else
                        goOnDie(gameTime);
                }
                for (int i = 0; i < 4; i++)
                {
                    if (tabFantome[i].BougerFantome(mesSommets, pacman) == 1)
                    {
                        if (tabFantome[i].Mvt is MouvementFuite)
                        {
                            score += 100;
                            tabFantome[i].reInitialize();
                        } 
                        else
                            goOnDie(gameTime);
                    }
                }
                // Verification fin niveau
                if (beanCpt <= 0)
                    NewLevel();
            }

            base.Update(gameTime);
        }
        //Update()


        
        /// <summary>
        /// Appelé quand le jeu doit se dessiner.
        /// </summary>
        /// <param name="gameTime">Fournit un aperçu des valeurs de temps.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Black);
            spriteBatch.Begin();

            //Création du décors
            for (int y = 0; y < map.GetLength(0); y++)
                for (int x = 0; x < map.GetLength(1); x++)
                {
                    if (map[y, x] == 0)
                        spriteBatch.Draw(mur, new Vector2(x * 20, y * 20), Color.White);
                    else if (map[y, x] == 1)
                        spriteBatch.Draw(bean, new Vector2(x * 20, y * 20), Color.White);
                    else if (map[y, x] == 3)
                        spriteBatch.Draw(gros_bean, new Vector2(x * 20, y * 20), Color.White);
                }
            for (int i = 0; i < vies; i++)
                spriteBatch.Draw(tVies[i], new Vector2(29 * 20 + i * 40, 10), Color.White);

            for (int i = 0; i < 4; i++)
                spriteBatch.Draw(tabFantome[i].Texture, tabFantome[i].Position, Color.White);
            spriteBatch.Draw(pacman.Texture, pacman.Position, Color.White);

            //Textes
            spriteBatch.DrawString(texteLevel.Texture, "Level : " + level, texteLevel.Position, texteLevel.Color);
            spriteBatch.DrawString(texteScore.Texture, "Score : " + score, texteScore.Position, texteScore.Color);
            spriteBatch.DrawString(texteToStart.Texture, contenuTexteToStart, texteToStart.Position, texteToStart.Color);
            spriteBatch.DrawString(texteGameOver.Texture, contenuTexteGameOver, texteGameOver.Position, texteGameOver.Color);


            spriteBatch.End();
            base.Draw(gameTime);
        }//Draw()




        /**********************************************************************************************************************/






        /// <summary>
        /// Appellé à chaque nouveau niveau
        /// </summary>
        /// <param name="type"> 0 : niveau suivant, 1 : niveau perdu, 2 : nouvelle partie </param>
        private void NewLevel(int type = 0)
        {
            if (type == 2)
            {
                level = 1;
                score = 0;
                vies = 3;
                contenuTexteToStart = "Tapez Entrer pour commencer.";
                havePause = haveStart = false;
                contenuTexteGameOver = "";
            }
            else
            {
                if (type == 0)
                {
                    level++;
                    this.TargetElapsedTime = TimeSpan.FromMilliseconds(TargetElapsedTime.TotalMilliseconds - TargetElapsedTime.TotalMilliseconds / 5);
                }
                else if (type == 1)
                {
                    if (--vies == 0) { 
                        contenuTexteGameOver = "Game Over";
                        contenuTexteToStart = "Tapez Entrer pour recommencer.";
                        haveStart = false;
                    }
                }
            }

            stateDead = 0;
            stateDecompteStart = 3;

            isClignotement = false;
            isFantomePeur = false;

            timeEndDecompte = TimeSpan.Zero; // Timer
            timeFinFantomePeur = TimeSpan.Zero; // timing fin 'fantome eur'

            if (type != 1)
            {
                map = new byte[,]{
                    //              5              10             15             20             25
                    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                    {0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 0}, //26
                    {0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0}, //6
                    {0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0}, //6
                    {0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0}, //6
                    {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, //26 //5
                    {0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0}, //6
                    {0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0}, //6
                    {0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0}, //20
                    {0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, //4
                    {0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, //4 //10
                    {0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0}, //12
                    {0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 4, 4, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0}, //4
                    {0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 4, 4, 4, 4, 4, 4, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0}, //4
                    {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 4, 4, 4, 4, 4, 4, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, //18
                    {0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 4, 4, 4, 4, 4, 4, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0}, //4 //15
                    {0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0}, //4
                    {0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0}, //12
                    {0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0}, //4
                    {0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0}, //4
                    {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, //24 //20
                    {0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0}, //6
                    {0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0}, //6
                    {0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0}, //22
                    {0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0}, //6
                    {0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0}, //6 //25 
                    {0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0}, //20
                    {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}, //4
                    {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}, //4
                    {0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 0}, //26
                    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //30
                };
                beanCpt = INITIAL_NB_BEAN; // 26*3 + 6*9 + 20*2 + 4*10 + 12*2 + 18 + 24
            }

            //réinitialisation du pacman
            if (pacman != null) pacman.initialize();

            //réinitialisation des fantomes
            for (int i = 0; i < 4; i++)
            {
                if (tabFantome != null && tabFantome[i] != null)
                    tabFantome[i].reInitialize();
            }

            if (tabFantome != null)
            {
                for (int i = 0; i < 4; i++)
                {
                    tabFantome[i].setMouv("A");
                }
            }
        } // NewLevel()


        /// <summary>
        /// Lorsque le pacman mange un gros bean
        /// </summary>
        public void goOnFantomePeur(bool isGoOn = true)
        {
            for (int i = 0; i < 4; i++)
            {
                if (tabFantome[i].Mvt != null)
                {
                    if (isGoOn)
                    {
                        tabFantome[i].fantomePeur();
                        tabFantome[i].setMouv("F");
                        tabFantome[i].Mvt.changerVitesse(true); // Ralenti
                    }
                    else
                    {
                        tabFantome[i].Mvt.changerVitesse(false);
                        tabFantome[i].fantomeRecolorer();
                    }
                }
            }
            isFantomePeur = isGoOn;
            isClignotement = false;
        } //goOnFantomePeur()


        public void goOnDie(GameTime gameTime)
        {
            timeEndDecompte = gameTime.TotalGameTime + TimeSpan.FromTicks(pacman.goOnDie(stateDead++).Ticks / 6);
        }



        struct TextePositionne
        {
            public SpriteFont Texture;    //  sprite texture 
            public Vector2 Position;     //  sprite position on screen
            public String Texte;
            public Color Color;

            public TextePositionne(SpriteFont texture, string texte, Vector2 position, Color color)
            {
                Texture = texture; Position = position; Texte = texte; Color = color;
            }
        }


        /// <summary>
        /// Getters, setters
        /// </summary>

        //car l'objet Content est inaccessible ailleur
        public Texture2D getTexture2D(String adr)
        {
            Texture2D texture2d = Content.Load<Texture2D>(adr);
            return texture2d;
        }
        public SoundEffect getSoundEffect(String adr)
        {
            SoundEffect son = Content.Load<SoundEffect>(adr);
            return son;
        }

        public byte[,] Map
        {
            get { return this.map; }
            set { this.map = value; }
        }

        internal Pacman Pacman
        {
            get { return pacman; }
            set { pacman = value; }
        }

        public int BeanCpt
        {
            get { return beanCpt; }
            set { beanCpt = value; }
        }

        public int Score
        {
            get { return score; }
            set { score = value; }
        }

        public bool IsClignotement
        {
            get { return isClignotement; }
            set { isClignotement = value; }
        }

        internal Fantome[] TabFantome
        {
            get { return tabFantome; }
            set { tabFantome = value; }
        }

    }
}
